#include"command.hpp"

#define PROCESS_NUM 5 // 进程池中子进程数量

void writeCommand(pid_t pid, int fd, int command)
{
    // 下达命令，即在管道中写入数据
    int ret = write(fd, &command, sizeof(command));
    if(ret == -1)
    {
        perror("write");
        exit(2);
    }
    
    cout << "父进程[" << getpid() << "]向子进程[" << pid << "]下达命令(" << command_menu[command] << ")成功,对应fd为:" << fd << endl;
}

int readCommand(int fd, int& quit)
{
    // 子进程读取命令
    int command;
    int ret = read(fd, &command, sizeof(command));
    if(ret == -1)
    {
        perror("read");
        exit(3);
    }
    else if(ret == 0)
    {
        quit = 1;
        return -1;
    }
    assert(ret == sizeof(command));

    return command;
}

int main()
{
    // 进来先加载commands和commandMenu
    load();

    vector<pair<pid_t, int>> slots;

    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        int pipefd[2] = {0};
        int ret = pipe(pipefd);
        assert(ret == 0);
        (void)ret;
        // 打开的每一个匿名管道都要创建对应的子进程

        pid_t id = fork();// 创建子进程
        if(id == -1)
        {
            perror("fork");
            exit(1);
        }

        if(id == 0) // 子进程
        {
            // 子进程进行读取，关掉写端
            close(pipefd[1]);

            // 子进程不断接收命令并执行
            while(true)
            {
                int quit = 0;
                int command = readCommand(pipefd[0], quit);
                
                if(quit == 1)
                    break;

                if(command >= 0 && command < commands.size())
                {
                    commands[command]();
                }
                else
                {
                    cout << "命令非法" << endl;
                }
            }

            // 接收不到命令后再退出
            exit(0);// exit会自动关闭文件描述符，不必手动关
        }

        // 父进程进行写入，关闭读端
        close(pipefd[0]);
        // 存储键值对（pid : fd）
        slots.push_back(pair<pid_t, int>(id, pipefd[1]));
    }
    
    // 让生成的随机数更随机一点
    srand((unsigned int)time(nullptr) ^ getpid());
    // 父进程不断下达命令
    while(true)
    {
        // 选择哪一个子进程执行命令
        int childProcess = rand() % slots.size();
        // 选择哪个命令,这里为了测试就搞成随机的，也可以搞成先打印命令菜单，然后再选择命令
        int command = rand() % commands.size();
        // 下达命令
        writeCommand(slots[childProcess].first, slots[childProcess].second, command);

        // 休息一秒钟，继续下达
        sleep(1);
    }

    // 命令下达完毕，关闭写端
    for(int i = 0; i < slots.size(); ++i)
    {
        close(slots[i].second);
    }

    // 等待子进程退出
    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        waitpid(-1, nullptr, 0);
    }

    return 0;
}





// #include<iostream>
// using namespace std;
// #include<unistd.h>
// #include<assert.h>

// #include<vector>




// #define PROCESS_NUM 5

// int main()
// {
    
//     vector<pair<pid_t, int>> slots;
    

//     // 创建多个管道
//     for(int i = 0; i < PROCESS_NUM; ++i) // 循环5次打开管道
//     {
//         int pipefd[2] = {0};
//         int n = pipe(pipefd);
//         assert(n == 0);
//         (void)n;

//         // 管道打开后，创建子进程
//         pid_t id = fork();
//         assert(id != -1);
//         if(id == 0)
//         {
//             //子进程关闭写端
//             close(pipefd[1]);
//             while(true)
//             {

//             }

//             exit(0);
//         }


//         // 父进程写入信息,关闭读端
//         close(pipefd[0]);
//         slots.push_back(pair<pid_t, int>(id, pipefd[1]));
//     }

//     // 写入命令

//     return 0;
// }
